"""
安全模块 - 数据模型
提供全面的安全审计和权限控制功能
"""
from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.core.cache import cache
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
import uuid
import hashlib
import json

from apps.core.models import TimeStampedModel

User = get_user_model()


class SecurityAuditLog(TimeStampedModel):
    """
    安全审计日志
    记录所有安全相关的重要事件
    """
    class EventType(models.TextChoices):
        LOGIN_SUCCESS = 'login_success', '登录成功'
        LOGIN_FAILED = 'login_failed', '登录失败'
        LOGOUT = 'logout', '登出'
        PASSWORD_CHANGE = 'password_change', '密码修改'
        PASSWORD_RESET = 'password_reset', '密码重置'
        ROLE_CHANGE = 'role_change', '角色变更'
        PERMISSION_CHANGE = 'permission_change', '权限变更'
        SUSPICIOUS_ACTIVITY = 'suspicious_activity', '可疑活动'
        DATA_ACCESS = 'data_access', '数据访问'
        DATA_MODIFICATION = 'data_modification', '数据修改'
        SYSTEM_ERROR = 'system_error', '系统错误'
        SECURITY_VIOLATION = 'security_violation', '安全违规'

    event_type = models.CharField('事件类型', max_length=30, choices=EventType.choices)
    event_name = models.CharField('事件名称', max_length=200)
    description = models.TextField('事件描述')

    # 用户信息
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='security_logs',
        verbose_name='用户',
        null=True,
        blank=True
    )
    username = models.CharField('用户名', max_length=150, null=True, blank=True)

    # 请求信息
    ip_address = models.GenericIPAddressField('IP地址', null=True, blank=True)
    user_agent = models.TextField('用户代理', blank=True)
    request_method = models.CharField('请求方法', max_length=10, blank=True)
    request_path = models.CharField('请求路径', max_length=500, blank=True)
    http_status = models.IntegerField('HTTP状态', null=True, blank=True)

    # 地理位置
    country = models.CharField('国家', max_length=50, blank=True)
    city = models.CharField('城市', max_length=100, blank=True)
    location = models.JSONField('位置信息', default=dict, blank=True)

    # 设备信息
    device_fingerprint = models.CharField('设备指纹', max_length=255, blank=True)
    device_type = models.CharField('设备类型', max_length=50, blank=True)
    browser = models.CharField('浏览器', max_length=100, blank=True)
    os = models.CharField('操作系统', max_length=100, blank=True)

    # 风险评估
    risk_level = models.CharField(
        '风险等级',
        max_length=20,
        choices=[
            ('low', '低风险'),
            ('medium', '中风险'),
            ('high', '高风险'),
            ('critical', '严重风险'),
        ],
        default='low'
    )
    risk_score = models.IntegerField('风险评分', default=0, help_text='0-100')

    # 关联对象
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        null=True,
        blank=True
    )
    object_id = models.UUIDField(null=True, blank=True)
    related_object = models.GenericForeignKey('content_type', 'object_id')

    # 额外信息
    metadata = models.JSONField('元数据', default=dict, blank=True)
    session_id = models.CharField('会话ID', max_length=100, blank=True)

    class Meta:
        db_table = 'security_audit_logs'
        verbose_name = '安全审计日志'
        verbose_name_plural = '安全审计日志'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['event_type', 'created_at']),
            models.Index(fields=['user', 'created_at']),
            models.Index(fields=['ip_address', 'created_at']),
            models.Index(fields=['risk_level', 'created_at']),
            models.Index(fields=['created_at']),
        ]

    def __str__(self):
        return f"{self.get_event_type_display()} - {self.username or 'System'}"

    def calculate_risk(self):
        """计算事件风险评分"""
        risk_score = 0

        # 基于事件类型
        type_risks = {
            self.EventType.LOGIN_FAILED: 30,
            self.EventType.SUSPICIOUS_ACTIVITY: 70,
            self.EventType.SECURITY_VIOLATION: 90,
            self.EventType.DATA_MODIFICATION: 40,
            self.EventType.ROLE_CHANGE: 60,
        }
        risk_score += type_risks.get(self.event_type, 10)

        # 基于IP地理位置
        if self.location and self.location.get('is_anonymous', False):
            risk_score += 20

        # 基于时间模式
        event_hour = self.created_at.hour
        if event_hour < 6 or event_hour > 22:  # 异常时间
            risk_score += 15

        # 基于设备指纹
        if not self.device_fingerprint:
            risk_score += 10

        self.risk_score = min(100, risk_score)

        # 设置风险等级
        if self.risk_score >= 80:
            self.risk_level = 'critical'
        elif self.risk_score >= 60:
            self.risk_level = 'high'
        elif self.risk_score >= 30:
            self.risk_level = 'medium'
        else:
            self.risk_level = 'low'

        self.save(update_fields=['risk_score', 'risk_level'])


class SecurityPolicy(TimeStampedModel):
    """
    安全策略
    定义系统的安全规则和策略
    """
    class PolicyType(models.TextChoices):
        PASSWORD = 'password', '密码策略'
        SESSION = 'session', '会话策略'
        ACCESS_CONTROL = 'access_control', '访问控制'
        DATA_PROTECTION = 'data_protection', '数据保护'
        MONITORING = 'monitoring', '监控策略'
        AUDIT = 'audit', '审计策略'

    name = models.CharField('策略名称', max_length=200)
    policy_type = models.CharField('策略类型', max_length=30, choices=PolicyType.choices)
    description = models.TextField('策略描述')

    # 策略配置
    configuration = models.JSONField('策略配置', default=dict)
    rules = models.JSONField('规则列表', default=list)

    # 状态管理
    is_active = models.BooleanField('是否启用', default=True)
    priority = models.IntegerField('优先级', default=1, help_text='1-10，数字越大优先级越高')

    # 生效范围
    apply_to_all_users = models.BooleanField('适用于所有用户', default=True)
    target_users = models.ManyToManyField(
        User,
        related_name='security_policies',
        verbose_name='目标用户',
        blank=True
    )
    target_roles = models.JSONField('目标角色', default=list, blank=True)

    # 时间设置
    effective_date = models.DateTimeField('生效时间', null=True, blank=True)
    expiry_date = models.DateTimeField('失效时间', null=True, blank=True)

    # 创建和修改
    created_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='created_security_policies',
        verbose_name='创建者'
    )

    class Meta:
        db_table = 'security_policies'
        verbose_name = '安全策略'
        verbose_name_plural = '安全策略'
        ordering = ['-priority', '-created_at']
        indexes = [
            models.Index(fields=['policy_type', 'is_active']),
            models.Index(fields=['priority']),
            models.Index(fields=['is_active']),
        ]

    def __str__(self):
        return self.name

    def is_effective(self):
        """检查策略是否在有效期内"""
        now = timezone.now()

        if self.effective_date and now < self.effective_date:
            return False

        if self.expiry_date and now > self.expiry_date:
            return False

        return self.is_active

    def applies_to_user(self, user):
        """检查策略是否适用于特定用户"""
        if self.apply_to_all_users:
            return True

        if user in self.target_users.all():
            return True

        if user.role in self.target_roles:
            return True

        return False


class AccessControl(TimeStampedModel):
    """
    访问控制
    细粒度的权限控制
    """
    class Permission(models.TextChoices):
        READ = 'read', '读取'
        WRITE = 'write', '写入'
        DELETE = 'delete', '删除'
        EXECUTE = 'execute', '执行'
        ADMIN = 'admin', '管理'

    class Resource(models.TextChoices):
        USER = 'user', '用户'
        COURSE = 'course', '课程'
        EXAM = 'exam', '考试'
        QUESTION = 'question', '题目'
        GRADE = 'grade', '成绩'
        ANALYTICS = 'analytics', '分析'
        SYSTEM = 'system', '系统'

    # 被授权用户
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='access_controls',
        verbose_name='用户'
    )

    # 权限信息
    permission = models.CharField('权限', max_length=20, choices=Permission.choices)
    resource_type = models.CharField('资源类型', max_length=20, choices=Resource.choices)
    resource_id = models.UUIDField('资源ID', null=True, blank=True)

    # 访问控制
    is_granted = models.BooleanField('是否授权', default=True)
    access_level = models.IntegerField('访问级别', default=1)

    # 限制条件
    ip_whitelist = models.JSONField('IP白名单', default=list, blank=True)
    time_restrictions = models.JSONField('时间限制', default=dict, blank=True)
    location_restrictions = models.JSONField('位置限制', default=dict, blank=True)

    # 有效期
    valid_from = models.DateTimeField('生效时间', null=True, blank=True)
    valid_until = models.DateTimeField('失效时间', null=True, blank=True)

    # 审计信息
    granted_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        related_name='granted_permissions',
        verbose_name='授权人',
        null=True,
        blank=True
    )
    reason = models.TextField('授权原因', blank=True)

    class Meta:
        db_table = 'access_controls'
        verbose_name = '访问控制'
        verbose_name_plural = '访问控制'
        unique_together = [['user', 'permission', 'resource_type', 'resource_id']]
        indexes = [
            models.Index(fields=['user', 'permission']),
            models.Index(fields=['resource_type', 'resource_id']),
            models.Index(fields=['is_granted']),
            models.Index(fields=['valid_until']),
        ]

    def __str__(self):
        return f"{self.user.real_name} - {self.get_permission_display()} - {self.get_resource_type_display()}"

    def is_valid(self):
        """检查权限是否仍然有效"""
        now = timezone.now()

        if self.valid_from and now < self.valid_from:
            return False

        if self.valid_until and now > self.valid_until:
            return False

        return self.is_granted

    def can_access(self, request=None):
        """检查是否可以访问资源"""
        if not self.is_valid():
            return False

        # IP白名单检查
        if request and self.ip_whitelist:
            client_ip = self._get_client_ip(request)
            if client_ip not in self.ip_whitelist:
                return False

        # 时间限制检查
        if request and self.time_restrictions:
            current_time = timezone.now()
            if not self._check_time_restrictions(current_time):
                return False

        return True

    def _get_client_ip(self, request):
        """获取客户端IP地址"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

    def _check_time_restrictions(self, current_time):
        """检查时间限制"""
        restrictions = self.time_restrictions

        # 工作日限制
        if restrictions.get('weekdays_only'):
            if current_time.weekday() >= 5:  # 周六、周日
                return False

        # 时间段限制
        start_time = restrictions.get('start_time')
        end_time = restrictions.get('end_time')
        if start_time and end_time:
            current_time_only = current_time.time()
            if not (start_time <= current_time_only <= end_time):
                return False

        return True


class DataEncryption(TimeStampedModel):
    """
    数据加密管理
    管理敏感数据的加密状态
    """
    class EncryptionType(models.TextChoices):
        AES256 = 'aes256', 'AES-256'
        RSA = 'rsa', 'RSA'
        HASH = 'hash', '哈希'
        SALT = 'salt', '加盐'

    class DataType(models.TextChoices):
        PERSONAL_INFO = 'personal_info', '个人信息'
        ACADEMIC_RECORD = 'academic_record', '学术记录'
        FINANCIAL = 'financial', '财务信息'
        HEALTH = 'health', '健康信息'
        SYSTEM = 'system', '系统信息'

    # 关联数据
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        verbose_name='内容类型'
    )
    object_id = models.UUIDField('对象ID')
    related_object = models.GenericForeignKey('content_type', 'object_id')

    # 加密信息
    field_name = models.CharField('字段名', max_length=100)
    data_type = models.CharField('数据类型', max_length=30, choices=DataType.choices)
    encryption_type = models.CharField('加密类型', max_length=20, choices=EncryptionType.choices)

    # 加密状态
    is_encrypted = models.BooleanField('是否已加密', default=True)
    encryption_key_id = models.CharField('密钥ID', max_length=100)
    encryption_algorithm = models.CharField('加密算法', max_length=100)
    key_version = models.IntegerField('密钥版本', default=1)

    # 审计信息
    encrypted_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        related_name='encrypted_data',
        verbose_name='加密操作者',
        null=True,
        blank=True
    )
    encryption_time = models.DateTimeField('加密时间', auto_now_add=True)

    # 访问控制
    access_log = models.JSONField('访问日志', default=list, blank=True)
    last_accessed = models.DateTimeField('最后访问时间', null=True, blank=True)

    class Meta:
        db_table = 'data_encryptions'
        verbose_name = '数据加密'
        verbose_name_plural = '数据加密'
        unique_together = [['content_type', 'object_id', 'field_name']]
        indexes = [
            models.Index(fields=['data_type', 'is_encrypted']),
            models.Index(fields=['encryption_type']),
            models.Index(fields=['encryption_time']),
            models.Index(fields=['last_accessed']),
        ]

    def __str__(self):
        return f"{self.data_type} - {self.field_name}"

    def log_access(self, user, access_type='read'):
        """记录访问日志"""
        access_record = {
            'user_id': str(user.id),
            'username': user.username,
            'access_type': access_type,
            'timestamp': timezone.now().isoformat(),
            'ip_address': getattr(user, 'last_login_ip', None),
        }

        self.access_log.append(access_record)
        self.last_accessed = timezone.now()
        self.save(update_fields=['access_log', 'last_accessed'])


class SecurityIncident(TimeStampedModel):
    """
    安全事件
    记录和处理安全相关的事件
    """
    class IncidentType(models.TextChoices):
        UNAUTHORIZED_ACCESS = 'unauthorized_access', '未授权访问'
        DATA_BREACH = 'data_breach', '数据泄露'
        MALICIOUS_ACTIVITY = 'malicious_activity', '恶意活动'
        SYSTEM_VULNERABILITY = 'system_vulnerability', '系统漏洞'
        POLICY_VIOLATION = 'policy_violation', '策略违规'
        SUSPICIOUS_PATTERN = 'suspicious_pattern', '可疑模式'

    class Severity(models.TextChoices):
        LOW = 'low', '低'
        MEDIUM = 'medium', '中'
        HIGH = 'high', '高'
        CRITICAL = 'critical', '严重'

    title = models.CharField('事件标题', max_length=200)
    incident_type = models.CharField('事件类型', max_length=50, choices=IncidentType.choices)
    severity = models.CharField('严重程度', max_length=20, choices=Severity.choices, default=Severity.MEDIUM)

    # 事件描述
    description = models.TextField('事件描述')
    impact_assessment = models.TextField('影响评估', blank=True)
    affected_assets = models.JSONField('受影响资产', default=list, blank=True)

    # 状态管理
    status = models.CharField(
        '状态',
        max_length=20,
        choices=[
            ('open', '开放'),
            ('investigating', '调查中'),
            ('resolved', '已解决'),
            ('closed', '已关闭'),
        ],
        default='open'
    )

    # 处理信息
    assigned_to = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        related_name='assigned_incidents',
        verbose_name='分配给',
        null=True,
        blank=True
    )

    # 时间线
    detected_at = models.DateTimeField('检测时间', auto_now_add=True)
    resolved_at = models.DateTimeField('解决时间', null=True, blank=True)
    closed_at = models.DateTimeField('关闭时间', null=True, blank=True)

    # 响应措施
    response_actions = models.JSONField('响应措施', default=list, blank=True)
    preventive_measures = models.JSONField('预防措施', default=list, blank=True)

    # 关联日志
    related_logs = models.ManyToManyField(
        SecurityAuditLog,
        related_name='related_incidents',
        verbose_name='相关日志',
        blank=True
    )

    # 报告
    incident_report = models.TextField('事件报告', blank=True)
    lessons_learned = models.TextField('经验教训', blank=True)

    class Meta:
        db_table = 'security_incidents'
        verbose_name = '安全事件'
        verbose_name_plural = '安全事件'
        ordering = ['-detected_at']
        indexes = [
            models.Index(fields=['incident_type', 'status']),
            models.Index(fields=['severity', 'status']),
            models.Index(fields=['detected_at']),
            models.Index(fields=['assigned_to']),
        ]

    def __str__(self):
        return f"{self.title} - {self.get_severity_display()}"

    def resolve(self, report_text='', preventive_text=''):
        """解决安全事件"""
        self.status = 'resolved'
        self.resolved_at = timezone.now()
        self.incident_report = report_text
        self.preventive_measures = json.loads(preventive_text) if isinstance(preventive_text, str) else preventive_text
        self.save(update_fields=['status', 'resolved_at', 'incident_report', 'preventive_measures'])

    def close(self, lessons_text=''):
        """关闭安全事件"""
        self.status = 'closed'
        self.closed_at = timezone.now()
        self.lessons_learned = lessons_text
        self.save(update_fields=['status', 'closed_at', 'lessons_learned'])